package Project;

import java.io.*;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;

public class ServerThread extends Thread
{
    private Socket socket;
    public ServerThread(Socket socket)
    {
        this.socket=socket;
    }
    @Override
    public void run()
    {
       //因为一个子线程负责处理整个客户端和服务器的交流，所以客户端发的昵称或者群聊都由这个子线程负责处理。
        // 可以由子线程在上传给服务端（比较规范），你要是在主线程去接受昵称，群聊，逻辑不太严谨
        //因为消息种类由两种，那么客户端必须按照一定的规则发给服务端（协议）
        //比如先发1那么后续就是昵称消息，先发2就是群聊消息,3就是对于某个客户端的私聊消息
       // 对于服务端管道来说，我是不是要拿这个管道的输入流呢，输入给自己
          try
        {
            DataInputStream dis=new DataInputStream(socket.getInputStream());

            while(true)
            {
                int type=dis.readInt();//这不是用户输入的，是客户端按照协议自己发的标记
                switch(type)
                {
                    case 1:
                        //客户端发来昵称消息去接收
                        String Nickname=dis.readUTF();
                        Server.OnlineSockets.put(socket,Nickname);//把管道添加进在线管道集合
                        //更新全部客户端在线列表
                        UpdateClientOnlineUserList();
                        break;

                    case 2:
                        //客户端发来群聊消息去接收
                        String content = dis.readUTF();
                        UpdateClientMessage(content);
                        break;

                    case 3: break;
                    default: break;
                }

            }

            }
          catch (Exception e)
          {   //客户端断开的逻辑：

              //客户端的管道断开了，那么我服务端的管道也会接到关闭事件，那么我服务端也得把曾经这个与客户端连接的服务端管道移除掉。
              //这个与客户端连接的服务端管道可以说是与客户端的码头，都是通过他的
              Server.OnlineSockets.remove(socket);//把下线过的客户端从map移除
              UpdateClientOnlineUserList();//下线了，我也要更新所有客户端的在线列表
          }


    }

    private void UpdateClientMessage(String content)
    {
//一旦发消息就很快做这个函数，那么我就要拿到现在的时间就行，而且要把它格式化成谁 年月日，时分秒,后面还要跟一个星期几的形式
        StringBuilder sb=new StringBuilder();
        String name=Server.OnlineSockets.get(this.socket);
        LocalDateTime now=LocalDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a");
         String result=sb.append(name+" ").append(dtf.format(now)).append("\r\n")
                 .append(content).toString();

//s是已经与客户端管道连接上的服务端管道
        for(Socket s:Server.OnlineSockets.keySet())
        {
           try {
                //因为登录就会更新到onlinesocket这个集合，而输入的消息内容也是通过子线程的socket的输入流传给服务端的
                //只需要遍历集合找到名字就行

                DataOutputStream dos=new DataOutputStream(s.getOutputStream());
                dos.writeInt(2);
                dos.writeUTF(result);
                dos.flush();
               }
         catch (Exception e)
              {
              System.out.println("一个客户端下线了");
              }
        }
    }

    private void UpdateClientOnlineUserList()
    {
        //更新全部客户端在线列表,遍历OnlineSockets集合的value（不是key）然后遍历到的信息都发给服务端，
        //服务端转发给所有客户端
      Collection<String> onlineUsers=Server.OnlineSockets.values();//拿到所有的用户名

        //两种方法都行
//        for(String user:onlineUsers)
//        {
//            //转发给所有客户端:遍历已经和客户端管道连接上的服务端管道
////            for(Socket s:Server.OnlineSockets.keySet())
////            {
////
////                try {
////                    DataOutput dos=new DataOutputStream(s.getOutputStream());
////                    dos.writeUTF(user);
////                    }
////                catch (Exception e)
////                {
////                    e.printStackTrace();
////                }
////            }
//        }

       // 服务端不要关管道和各种流
        //转发给所有客户端管道（遍历已经和客户端管道连接上的服务端管道）
        for(Socket s:Server.OnlineSockets.keySet())
        {    //s是已经与客户端管道连接上的服务端管道    开始传人数列表（人名）给客户端
            try                            //服务端的数据已经改变了，我肯定要传相应的数据给到客户端
             {  DataOutputStream dos=new DataOutputStream(s.getOutputStream());//不要用多态父类DataOutput，刷新是实现类的独有方法
                dos.writeInt(1);//告诉客户端我给你传的是在线人数列表信息
                dos.writeInt(onlineUsers.size());//协议：给客户端先说好，我要更新的人数总数，之后我要打印这么多个user
                for(String user:onlineUsers)
                {
                    dos.writeUTF(user);
                }
               dos.flush();//防止在缓冲区里面，没传出去
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
          }
    }
}
